#pragma once

#include <string>

namespace cal
{

bool is_identifier(const std::string& str);

class Identifier
{
public:
    using size_type = std::string::size_type;

public:
    friend std::istream& operator>>(std::istream& is, Identifier& id);
    friend std::ostream& operator<<(std::ostream& os, const Identifier& id);
    friend bool          operator==(const Identifier& lhs, const Identifier& rhs) 
    {
        return lhs._val == rhs._val;
    }

public:
    Identifier() = default;
    Identifier(const std::string& str);
    ~Identifier() = default;

public:
    std::string str()  const { return _val; }
    size_type   size() const { return _val.size(); }

private:
    std::string _val;
};

std::istream& operator>>(std::istream& is, Identifier& id);
std::ostream& operator<<(std::ostream& os, const Identifier& id);

}   // namespace cal

namespace std 
{

template <>
struct hash<cal::Identifier> 
{
    std::size_t operator()(const cal::Identifier& id) const 
    {
        return std::hash<std::string>()(id.str());
    }
};

}   // namespace std
